#include "aerial\Core\NetIO.h"
#include "aerial\Core\NetMessage.h"

#include <iostream>

using namespace air;
using namespace std;

#include "raknet\RakString.h"

RakPeerInterface* NetIO::node = 0;
ThreadsafeAllocatingQueue<Packet> NetIO::Q;
HANDLE NetIO::hThreadRakNet = NULL;
bool NetIO::running = false;

NetIO::NetIO(): server(false), me(0), expectedPlayerCount(0)
{
	if (!node) node = RakPeerInterface::GetInstance();
	reset();
}

bool NetIO::IsValidIP(const char* str)
{
		int   dot_count = 0, num_count = 0, num_val = 0;
		while((*str)) 
		{ 
			if((*str) != '.') 
			{ 
				if((*str) <= '9' && (*str) >= '0') 
				{ 
					++num_count; 
					num_val = num_val * 10 + (*str) - '0'; 
				} 
				else return false; 
			}
			else 
			{
				++dot_count;
				if(num_count < 1 || num_count > 4 || num_val < 0 || num_val > 255) return false; 
				num_count = 0; 
				num_val = 0; 
			} 
			++str; 
		} 
		if (dot_count != 3) return false; 
		return true; 
	}


void NetIO::Release() {
	Q.Clear(_FILE_AND_LINE_);
	if (hThreadRakNet) {
		running = false;
		uint ret = WaitForSingleObject(hThreadRakNet, 300);
		if (ret == WAIT_TIMEOUT)
		{				
			cout << "Force Terminating Thread.\n";
			TerminateThread(hThreadRakNet, 0);
		}
		::CloseHandle(hThreadRakNet);
		hThreadRakNet = 0;	
	}
	node->Shutdown(300);
	RakPeerInterface::DestroyInstance(node);
}

void NetIO::reset() {
	ClearPlayerInfo();
	Q.Clear(_FILE_AND_LINE_);

	expectedPlayerCount = 0;
	readyCount = 0;
	confirmCount = 0;
	server = false;

	lock = false;
	me = 0;
}

bool NetIO::StartBattleServer()
{
	node->SetTimeoutTime(1000,UNASSIGNED_SYSTEM_ADDRESS);
	SocketDescriptor sd(BATTLE_SERVER_PORT, 0);
	sd.socketFamily = AF_INET;
	StartupResult sr = node->Startup(9, &sd, 1);
	if (sr != RAKNET_STARTED) return false;
#ifdef _DEBUG
	RakAssert(sr == RAKNET_STARTED);
#endif
	node->SetMaximumIncomingConnections(9);
	node->SetOccasionalPing(true);
	node->SetUnreliableTimeout(1000);
	cout << node->GetMyBoundAddress().ToString() << endl;
	return true;
}

bool NetIO::ConnectToBattleServer(const char* ip)
{
	SocketDescriptor sd(0, 0);
	sd.socketFamily = AF_INET;
	node->Startup(4, &sd, 1);
	node->SetOccasionalPing(true);
	node->SetTimeoutTime(1000, UNASSIGNED_SYSTEM_ADDRESS);
	node->AllowConnectionResponseIPMigration(true);
	cout << ip << "|" << BATTLE_SERVER_PORT << endl;
	ConnectionAttemptResult car = node->Connect(ip, BATTLE_SERVER_PORT, "", 0);
	return car == CONNECTION_ATTEMPT_STARTED;
#ifdef _DEBUG
	RakAssert(car==CONNECTION_ATTEMPT_STARTED);
#endif
	
}

int NetIO::findPlayerBySystemAddress(SystemAddress& sysAddr)
{
	for (int i = 0; i < MAX_PLAYER_COUNT; i++)
	{
		if (playerInfo[i].available && sysAddr == playerInfo[i].sa) return i;
	}
	return 255;
}

void NetIO::A_StartThread()
{
	hThreadRakNet = (HANDLE)_beginthreadex(NULL, 0, NetworkProc, (void*)0, 0, &NetWorkProcAddr);
	running = true;
}

void NetIO::A_TerminateThread()
{
	if (hThreadRakNet) {
		running = false;
		uint ret = WaitForSingleObject(hThreadRakNet, 300);
		if (ret == WAIT_TIMEOUT)
		{				
#ifdef _DEBUG
			cout << "Force Terminating Thread.\n";
#endif
			TerminateThread(hThreadRakNet, 0);
		}
		::CloseHandle(hThreadRakNet);
		hThreadRakNet = 0;	
	}
}

void NetIO::CloseConnection()
{
	node->CloseConnection(UNASSIGNED_SYSTEM_ADDRESS, true);
	node->Shutdown(300);
}

void NetIO::broadcastExcludePlayer(BitStream* bso, int playerId)
{
	if (playerId == -1) 	node->Send(bso, HIGH_PRIORITY, RELIABLE_ORDERED, 0, UNASSIGNED_SYSTEM_ADDRESS, true);
	else node->Send(bso, HIGH_PRIORITY, RELIABLE_ORDERED, 0, playerInfo[playerId].sa, true);
}

void NetIO::broadcastExcludeAddress(BitStream* bso, SystemAddress& sa)
{
	node->Send(bso, HIGH_PRIORITY, RELIABLE_ORDERED, 0, sa, true);
}

void NetIO::sendToBattleServer(BitStream* bso)
{
	if (server) 
		node->Send(bso, HIGH_PRIORITY, RELIABLE_ORDERED, 0, UNASSIGNED_SYSTEM_ADDRESS, true);
	else {
		node->Send(bso, HIGH_PRIORITY, RELIABLE_ORDERED, 0, battleServerAddress, false);
	}
}

void NetIO::SendToSpecifiedAddress(BitStream* bso, SystemAddress& sa)
{
	node->Send(bso, HIGH_PRIORITY, RELIABLE_ORDERED, 0, sa, false);
}

void NetIO::RegisterServerAddress(SystemAddress* sa)
{
	battleServerAddress = *sa;
}

int NetIO::AddPlayer(RakString userName, SystemAddress sa, int boardId)
{
	for (int i = 0; i < MAX_PLAYER_COUNT; i++) {
		if (!playerInfo[i].available) {
			playerInfo[i].available = true;
			playerInfo[i].userName = userName;
			playerInfo[i].boardId = boardId;
			playerInfo[i].sa = sa;
			if (boardId < MAX_PLAYER_COUNT / 2) playerInfo[i].side = 0;
			else playerInfo[i].side = 1;
			playerInfo[i].flyObjectType = 1;
			++expectedPlayerCount;
			return i;
		}
	}
	return 0;
}

void NetIO::m_Deallocate(Packet* p) {
	node->DeallocatePacket(p);
}

void NetIO::Deallocate(Packet* p)
{
	node->DeallocatePacket( p );
}

Packet* NetIO::recv()
{	
	Packet* p;
	// MUST be Deallocated by user by calling node->DeallocatePacket(p);
	if (server)
	{
		if (!Q.IsEmpty()) {
			return Q.Pop();
		}
		return 0;
	}
	else 
	{
		p = node->Receive();	
		if (p) return p;
		return 0;
	}
}

Packet* NetIO::m_recv()
{	
	Packet* p;
	// MUST be Deallocated by user by calling node->DeallocatePacket(p);
	p = node->Receive();
	if (p) return p;
	return 0;
}

unsigned int __stdcall NetIO::NetworkProc(void *pParam)
{
	Packet* p;
	while (true) {
		if (!running) return 0;
		// MUST be Deallocated by user by calling node->DeallocatePacket(p)
		if (p = node->Receive())
		{
			if (p->data[0] > AIR_SEP) 
			{
				BitStream bsForward(p->data, p->length, false);
				node->Send(&bsForward, HIGH_PRIORITY, RELIABLE_ORDERED, 0, p->systemAddress, true);
				Q.Push(p);
			}
			else {
				if (p->data[0] == ID_NEW_INCOMING_CONNECTION) node->CloseConnection( p->systemAddress, true );
				else {
					Q.Push(p);
				}
			}
		}
		Sleep(1);
	}
	return 0;
}

void NetIO::ClearPlayerInfo()
{
	for ( int i = 0; i < MAX_PLAYER_COUNT; i++ )
	{
		playerInfo[i].confirm = false;
		playerInfo[i].available = false;
		playerInfo[i].ready = false;
	}
}

int NetIO::DeserializeSinglePlayerInfo(BitStream* bsi, bool head)
{
	if (head) {
		uchar whead = 0;
		bsi->Read(whead);
	}
	uchar id;
	bsi->Read(id);
	bsi->Read(playerInfo[id].boardId);
	RakString sa; sa.Deserialize(bsi); playerInfo[id].sa.FromString(sa);
	bsi->Read(playerInfo[id].side);
	playerInfo[id].userName.Deserialize(bsi);
	playerInfo[id].available = true;
	return id;
}

void NetIO::DeSerializationPlayerInfo(BitStream* bsi, bool head)
{
	if (head) {
		uchar whead = 0;
		bsi->Read(whead);
	}
	bsi->Read(expectedPlayerCount);
	bsi->Read(readyCount);
	for ( int i = 0; i < MAX_PLAYER_COUNT; i++ )
	{
		bsi->Read(playerInfo[i].available);
		if (playerInfo[i].available)
		{
			bsi->Read(playerInfo[i].boardId);
			bsi->Read(playerInfo[i].flyObjectType);
			bsi->Read(playerInfo[i].ready);
			RakString sa;
			sa.Deserialize(bsi);
			playerInfo[i].sa.FromString(sa);
			bsi->Read(playerInfo[i].side);
			playerInfo[i].userName.Deserialize(bsi);
		}
	}
}

void NetIO::SerializeSinglePlayerInfo(BitStream* bso, uchar id, bool head)
{
	if (head) bso->Write((uchar)ID_INFO_INCREMENT);
	bso->Write(id);
	bso->Write(playerInfo[id].boardId);
	RakString::Serialize(playerInfo[id].sa.ToString(), bso);
	bso->Write(playerInfo[id].side);
	playerInfo[id].userName.Serialize(bso);
}

void NetIO::SerializePlayerInfo(BitStream* bso, bool head)
{
	if (head) bso->Write((uchar)ID_INFO);
	bso->Write(expectedPlayerCount);
	bso->Write(readyCount);
	for ( int i = 0; i < MAX_PLAYER_COUNT; i++ )
	{
		bso->Write(playerInfo[i].available);
		if (playerInfo[i].available) 
		{
			bso->Write(playerInfo[i].boardId);
			bso->Write(playerInfo[i].flyObjectType);
			bso->Write(playerInfo[i].ready);
			RakString::Serialize(playerInfo[i].sa.ToString(), bso);
			bso->Write(playerInfo[i].side);
			playerInfo[i].userName.Serialize(bso);
		}
	}
}

NetIO::~NetIO() {}